package com.chrilwe.pigpig.auth.service;

import java.net.URI;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.client.RestTemplate;

import com.alibaba.fastjson.JSON;
import com.chrilwe.pigpig.auth.common.request.LoginRequest;
import com.chrilwe.pigpig.auth.common.response.LoginResult;
import com.chrilwe.pigpig.auth.common.response.ResultEnum;

import cn.chrilwe.pigpig.core.util.ServiceName;
import cn.chrilwe.pigpig.core.util.TokenStore;

@Service
public class AuthService {

	private static final String OAUTH_URI = "/oauth/token";

	@Autowired
	protected StringRedisTemplate stringRedisTemplate;

	@Autowired
	protected RestTemplate restTemplate;

	@Autowired
	private LoadBalancerClient loadBalancerClient;

	@Autowired
	protected ValidateCodeService validateCodeService;

	@Value("${encrypt.key-store.clientId}")
	protected String clientId;

	@Value("${encrypt.key-store.clientSecret}")
	protected String clientSecret;

	private static final long tokenExpire = 30l;//分钟

	public LoginResult doLogin(LoginRequest loginRequest) {
		if (loginRequest != null) {
			String code = loginRequest.getCode();
			String username = loginRequest.getUsername();
			String password = loginRequest.getPassword();

			if (StringUtils.isAnyEmpty(code, username, password)) {
				return new LoginResult(ResultEnum.ERROR_REQUEST_PARAM.getCode(),
						ResultEnum.ERROR_REQUEST_PARAM.getName(), "");
			}

			boolean check = checkValidateCode(code, loginRequest.getClientToken());
			if (!check) {
				return new LoginResult(ResultEnum.ERR_VALIDATE_CODE.getCode(), ResultEnum.ERR_VALIDATE_CODE.getName(),
						"");
			}

			TokenStore tokenStore = getTokenStore(username, password);
			if (tokenStore == null) {
				return new LoginResult(ResultEnum.UNAUTHORITY.getCode(), ResultEnum.UNAUTHORITY.getName(), "");
			}
			String accessToken = tokenStore.getAccessToken();
			return new LoginResult(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getName(), accessToken);
		}
		return new LoginResult(ResultEnum.UNKNOW_ERR.getCode(), ResultEnum.UNKNOW_ERR.getName(), "");
	}

	protected boolean checkValidateCode(String code, String clientId) {

		return validateCodeService.checkCode(clientId, code);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	protected TokenStore getTokenStore(String username, String password) {
		String authUrl = getAuthUrl();

		// 对clientId:clientSecret进行base64编码
		String target = clientId + ":" + clientSecret;
		byte[] encode = Base64Utils.encode(target.getBytes());

		LinkedMultiValueMap<String, String> header = new LinkedMultiValueMap<String, String>();
		header.add("Authorization", "Basic " + new String(encode));
		LinkedMultiValueMap<String, String> body = new LinkedMultiValueMap<String, String>();
		body.add("grant_type", "password");
		body.add("username", username);
		body.add("password", password);
		HttpEntity entity = new HttpEntity(body, header);
		ResponseEntity<Map> responseEntity = restTemplate.exchange(authUrl, HttpMethod.POST, entity, Map.class);
		Map res = responseEntity.getBody();
		if(res != null) {
			String jwtToken = (String)res.get("access_token");
			String refreshToken = (String)res.get("refresh_token");
			String accessToken = (String)res.get("jti");
			
			TokenStore store = new TokenStore();
			store.setAccessToken(accessToken);
			store.setJwtToken(jwtToken);
			store.setRefreshToken(refreshToken);
			return store;
		}
		return null;
	}

	protected void saveTokenStore(TokenStore store) {
		stringRedisTemplate.opsForValue().set(store.getAccessToken(), JSON.toJSONString(store), tokenExpire,
				TimeUnit.MINUTES);
	}

	protected String getAuthUrl() {
		ServiceInstance instance = loadBalancerClient.choose(ServiceName.PIGPIG_AUTH);
		URI host = instance.getUri();
		return host + OAUTH_URI;
	}
	
}
